package com.rc.saas.mini.config;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * 注解声明式事务
 * Spring AOP 配置全局事物
 * Created by sven on 2019/10/16
 */
@Aspect
@Configuration
public class TransactionAdviceConfig {
    private static final int TX_METHOD_TIMEOUT = 5;

    /*
     * 定义切入点
     * execution()是最常用的切点函数
     * execution (com.coolron.user.service.impl..*.*(..))
     * 1、execution(): 表达式主体。
     * 2、第一个*号：表示返回类型，*号表示所有的类型。
     * 3、包名：表示需要拦截的包名，后面的两个句点表示当前包和当前包的所有子包，com.sample.service.impl包、子孙包下所有类的方法。
     * 4、第二个*号：表示类名，*号表示所有的类。
     * 5、*(..):最后这个星号表示方法名，*号表示所有的方法，后面括弧里面表示方法的参数，两个句点表示任何参数。
     */
    private static final String AOP_POINTCUT_EXPRESSION = "execution(public * com.rc.saas.mini.service.*.*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    /**
     * 配置事务拦截器
     * @return
     */
    @Bean
    public TransactionInterceptor txAdvice() {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        /*只读事务，不做更新操作*/
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

        /*当前存在事务就使用当前事务，当前不存在事务就创建一个新的事务*/
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();

        /*Exception回滚*/
        requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(TX_METHOD_TIMEOUT);

        // <!-- 配置事务拦截器(rollback-for="java.lang.Exception") -->
        //<tx:advice id="txAdvice" transaction-manager="transactionManager">
        //    <tx:attributes>
        //        <tx:method name="create*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        //        <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        //        <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        //        <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        //        <tx:method name="sync*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
        //        <tx:method name="is*" read-only="true"/>
        //        <tx:method name="validate*" read-only="true"/>
        //        <tx:method name="find*" read-only="true"/>
        //        <tx:method name="get*" read-only="true"/>
        //    </tx:attributes>
        //</tx:advice>

        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("create*", requiredTx);
        txMap.put("insert*", requiredTx);
        txMap.put("save*", requiredTx);
        txMap.put("update*", requiredTx);
        txMap.put("delete*", requiredTx);
        txMap.put("sync*", requiredTx);
        txMap.put("is*", readOnlyTx);
        txMap.put("get*", readOnlyTx);
        txMap.put("find*", readOnlyTx);
        txMap.put("query*", readOnlyTx);
        txMap.put("validate*", readOnlyTx);

        source.setNameMap(txMap);
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
        return txAdvice;
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}
